package com.wsoft.oss.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.wsoft.constant.CommonConstant;
import com.wsoft.core.exception.Asserts;
import com.wsoft.oss.entity.OssFileEntity;
import com.wsoft.oss.enums.BucketNameEnum;
import com.wsoft.oss.enums.FileSizeEnum;
import com.wsoft.utils.FileUtil;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;

/**
 * @Author z001
 * @Date 2024/10/12 14:51
 */
public class OssUtil {

    /**
     * 临时字段
     */
    public static final String TEMPORARY = "temporary";
    /**
     * 分片上传临时文件目录
     */
    public static final String TEMPORARY_URL = StrUtil.SLASH + TEMPORARY + StrUtil.SLASH;
    /**
     * 分片 临时后缀
     */
    public static final String PART = ".part";
    /**
     * 截取 part 正则
     */
    public static final String PART_PATTERN = "\\.part(\\d+)";
    /**
     * 前端 bytes=
     */
    private static final String RANGE_BYTES = "bytes=";

    private static final Integer TWO = 2;

    /**
     * 附件 日期格式化字符串
     */
    private static final String DATE_FORMAT = "yyyyMM";


    /**
     * 获取新的uuid
     *
     * @return
     */
    public static String getUuid() {
        return RandomUtil.randomNumbers(18);
    }

    /**
     * 格式化日期
     *
     * @return
     */
    public static String getDateFormat() {
        return DateUtil.format(DateUtil.date(), DATE_FORMAT);
    }

    /**
     * 将文件名 替换成uuid 生成新的文件名
     *
     * @param originalFilename 原名称
     * @param uuid             uuid
     * @return
     */
    public static String changeNewFileName(String originalFilename, String uuid) {
        // 获取文件后缀名
        String extension = originalFilename.substring(originalFilename.lastIndexOf(StrUtil.DOT) + 1);
        // 生成UUID作为新的文件名
        return uuid + StrUtil.DOT + extension;
    }


    /**
     * 构建minio objectName
     *
     * @param uuid     uuid
     * @param fileType 文件后缀
     * @return
     */
    public static String buildMinioObjectName(String uuid, String fileType) {
        return StrUtil.SLASH + getDateFormat() + StrUtil.SLASH + uuid + StrUtil.DOT + fileType;
    }

    /**
     * 构建 本地上传目录 地址
     *
     * @param uploadPath 上传路径
     * @param bucketType 桶类型
     * @return 最后带`/`
     */
    public static String buildLocalDirPath(String uploadPath, Integer bucketType) {
        String bucketName = BucketNameEnum.resolve(bucketType).getBucketName();
        return buildLocalDirPath(uploadPath,bucketName);
    }

    /**
     * 构建 本地上传目录 地址
     *
     * @param uploadPath 上传路径
     * @param bucketName 桶名称
     * @return 最后带`/`
     */
    public static String buildLocalDirPath(String uploadPath, String bucketName) {
        return uploadPath + bucketName + StrUtil.SLASH + getDateFormat() + StrUtil.SLASH;
    }


    /**
     * 构建 minio 上传目录地址
     *
     * @return
     */
    public static String buildMinioDirPath() {
        return StrUtil.SLASH + getDateFormat() + StrUtil.SLASH;
    }

    /**
     * 构建 minio上传目录 临时地址
     *
     * @param md5 md5值
     * @return 最后带`/`
     */
    public static String buildMinioDirTemporaryPath(String md5) {
        return TEMPORARY_URL + md5 + StrUtil.SLASH;
    }

    /**
     * 构建 本地上传目录 临时地址
     *
     * @param uploadPath 上传路径
     * @param bucketType 桶类型
     * @param md5        md5值
     * @return 最后带`/`
     */
    public static String buildLocalDirTemporaryPath(String uploadPath, Integer bucketType, String md5) {
        String bucketName = BucketNameEnum.resolve(bucketType).getBucketName();
        return uploadPath + bucketName + TEMPORARY_URL + md5 + StrUtil.SLASH;
    }

    /**
     * 构建 修改图片后的 名称
     *
     * @param uuid       uuid
     * @param sizeType   图片类型 MIN/MID/MAX
     * @param fileSuffix 后缀
     * @return
     */
    public static String buildChangePicName(String uuid, String sizeType, String fileSuffix) {
        return uuid + StrUtil.UNDERLINE + sizeType + StrUtil.DOT + fileSuffix;
    }

    /**
     * 构建 minio 修改图片后的 路径
     *
     * @param objectName
     * @return
     */
    public static String buildMinioChangePicUrl(String objectName) {
        return StrUtil.SLASH + getDateFormat() + StrUtil.SLASH + objectName;
    }

    /**
     * 构建 本地上传 文件名
     *
     * @param uuid       uuid
     * @param fileSuffix 文件后缀名
     * @return
     */
    public static String buildLocalFileName(String uuid, String fileSuffix) {
        if (StrUtil.isBlank(uuid)) {
            uuid = getUuid();
        }
        return uuid + StrUtil.DOT + fileSuffix;
    }


    /**
     * 获取分片名称 ==> 截取掉part等后缀,返回文件名
     *
     * @param partNameSet partName集合
     * @return 文件名
     */
    public static String getPartName(Set<Object> partNameSet) {
        String partName = partNameSet.stream().findAny().get().toString();
        // 找到".part"之前的位置
        int endIndex = partName.lastIndexOf(PART);
        return partName.substring(0, endIndex);
    }

    /**
     * 下载分片 构建开始和结束为止
     *
     * @param range    前端传上传分片开始和结束
     * @param fileSize 文件大小
     * @return
     */
    public static Map<String, Long> buildSize(String range, long fileSize) {

        Map<String, Long> sizeMap = new HashMap<>(2);
        // 开始下载位置
        long startByte = 0;
        // 结束下载位置
        long endByte = fileSize - 1;

        // 存在 range，需要根据前端下载长度进行下载，即分段下载
        // 例如：range=bytes=0-52428800
        if (range != null && range.contains(RANGE_BYTES) && range.contains(StrUtil.DASHED)) {
            // 0-52428800
            range = range.substring(range.lastIndexOf("=") + 1).trim();
            String[] ranges = range.split(StrUtil.DASHED);
            // 判断range的类型
            if (ranges.length == 1) {
                // 类型一：bytes=-2343 后端转换为 0-2343
                if (range.startsWith(StrUtil.DASHED)) {
                    endByte = Long.parseLong(ranges[0]);
                }
                // 类型二：bytes=2343- 后端转换为 2343-最后
                if (range.endsWith(StrUtil.DASHED)) {
                    startByte = Long.parseLong(ranges[0]);
                }
            } else if (ranges.length == TWO) {
                // 类型三：bytes=22-2343
                startByte = Long.parseLong(ranges[0]);
                endByte = Long.parseLong(ranges[1]);
            }
        }
        sizeMap.put("startByte", startByte);
        sizeMap.put("endByte", endByte);
        return sizeMap;
    }

    /**
     * 获取文件url
     *
     * @param fileEntity
     * @param picType
     * @return
     */
    public static String getFileUrl(OssFileEntity fileEntity, Integer picType) {
        FileSizeEnum resolve = FileSizeEnum.resolve(picType);
        String url = null;
        if (!fileEntity.getFileType().equals(FileUtil.IMAGE) || null == resolve) {
            url = fileEntity.getUrl();
        } else if (FileSizeEnum.MIN.getValue().equals(picType)) {
            url = fileEntity.getMinUrl();
        } else if (FileSizeEnum.MID.getValue().equals(picType)) {
            url = fileEntity.getMidUrl();
        } else {
            url = fileEntity.getMaxUrl();
        }
        return url;
    }

    /**
     * 校验文件大小
     *
     * @param multipartFileList
     * @param maxSize
     */
    public static void checkFileSize(MultipartFile[] multipartFileList, Integer maxSize) {
        double totalSize = Arrays.stream(multipartFileList)
                .filter(Objects::nonNull)
                .mapToLong(MultipartFile::getSize)
                .sum()
                / (1024 * 1024.0);
        if (totalSize > maxSize) {
            //文件超出最大限制
            Asserts.fail("[oss.file.maximum.limit]");
        }
    }

    /**
     * 获取系统 设置的文件最大值
     *
     * @param configCache redis中的值
     * @param maxSize     配置文件中的值
     * @return
     */
    public static int getFileMaxSize(Object configCache, Integer maxSize) {
        int fileMaxSize = 0;
        if (ObjUtil.isEmpty(configCache)) {
            fileMaxSize = maxSize;
        } else {
            fileMaxSize = (int) getOssRedisValue(configCache, CommonConstant.OSS_MAXIMUM_LIMIT);
        }
        return fileMaxSize;
    }


    /**
     * 获取redis oss json key
     *
     * @param configCache redis中的值
     * @param redisKey    key
     * @return
     */
    public static Object getOssRedisValue(Object configCache, String redisKey) {
        Map<String, Object> map = JSONObject.parseObject(JSONArray.parseArray(JSON.toJSONString(configCache)).get(0).toString());
        JSONObject fileMap = JSONObject.parseObject(map.get(CommonConstant.OSS_CONFIG_FILE).toString());
        return fileMap.get(redisKey);
    }

}
